/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import * as languageFacts from 'vscode-css-languageservice/services/languageFacts';
import * as nodes from 'vscode-css-languageservice/parser/cssNodes';
import { CSSCompletion } from 'vscode-css-languageservice/services/cssCompletion';
import { CompletionList, CompletionItemKind, InsertTextFormat, TextEdit, CompletionItem } from 'vscode-languageserver-types';

interface IFunctionInfo {
	name: string;
	example: string;
	description?: string;
	type?: string;
}

export class LESSCompletion extends CSSCompletion {

	private static builtInProposals: IFunctionInfo[] = [
		{
			'name': 'escape',
			'example': 'escape(@string);',
			'description': 'URL encodes a string'
		},
		{
			'name': 'e',
			'example': 'e(@string);',
			'description': 'escape string content'
		},
		{
			'name': 'replace',
			'example': 'replace(@string, @pattern, @replacement[, @flags]);',
			'description': 'string replace'
		},
		{
			'name': 'unit',
			'example': 'unit(@dimension, [@unit: \'\']);',
			'description': 'remove or change the unit of a dimension'
		},
		{
			'name': 'color',
			'example': 'color(@string);',
			'description': 'parses a string to a color',
			'type': 'color'
		},
		{
			'name': 'convert',
			'example': 'convert(@value, unit);',
			'description': 'converts numbers from one type into another'
		},
		{
			'name': 'data-uri',
			'example': 'data-uri([mimetype,] url);',
			'description': 'inlines a resource and falls back to `url()`',
			'type': 'url'
		},
		{
			'name': 'length',
			'example': 'length(@list);',
			'description': 'returns the number of elements in a value list'
		},
		{
			'name': 'extract',
			'example': 'extract(@list, index);',
			'description': 'returns a value at the specified position in the list'
		},
		{
			'name': 'abs',
			'description': 'absolute value of a number',
			'example': 'abs(number);'
		},
		{
			'name': 'acos',
			'description': 'arccosine - inverse of cosine function',
			'example': 'acos(number);'
		},
		{
			'name': 'asin',
			'description': 'arcsine - inverse of sine function',
			'example': 'asin(number);'
		},
		{
			'name': 'ceil',
			'example': 'ceil(@number);',
			'description': 'rounds up to an integer'
		},
		{
			'name': 'cos',
			'description': 'cosine function',
			'example': 'cos(number);'
		},
		{
			'name': 'floor',
			'description': 'rounds down to an integer',
			'example': 'floor(@number);'
		},
		{
			'name': 'percentage',
			'description': 'converts to a %, e.g. 0.5 > 50%',
			'example': 'percentage(@number);',
			'type': 'percentage'
		},
		{
			'name': 'round',
			'description': 'rounds a number to a number of places',
			'example': 'round(number, [places: 0]);'
		},
		{
			'name': 'sqrt',
			'description': 'calculates square root of a number',
			'example': 'sqrt(number);'
		},
		{
			'name': 'sin',
			'description': 'sine function',
			'example': 'sin(number);'
		},
		{
			'name': 'tan',
			'description': 'tangent function',
			'example': 'tan(number);'
		},
		{
			'name': 'atan',
			'description': 'arctangent - inverse of tangent function',
			'example': 'atan(number);'
		},
		{
			'name': 'pi',
			'description': 'returns pi',
			'example': 'pi();'
		},
		{
			'name': 'pow',
			'description': 'first argument raised to the power of the second argument',
			'example': 'pow(@base, @exponent);'
		},
		{
			'name': 'mod',
			'description': 'first argument modulus second argument',
			'example': 'mod(number, number);'
		},
		{
			'name': 'min',
			'description': 'returns the lowest of one or more values',
			'example': 'min(@x, @y);'
		},
		{
			'name': 'max',
			'description': 'returns the lowest of one or more values',
			'example': 'max(@x, @y);'
		}
	];

	private static colorProposals: IFunctionInfo[] = [
		{
			'name': 'argb',
			'example': 'argb(@color);',
			'description': 'creates a #AARRGGBB'
		},
		{
			'name': 'hsl',
			'example': 'hsl(@hue, @saturation, @lightness);',
			'description': 'creates a color'
		},
		{
			'name': 'hsla',
			'example': 'hsla(@hue, @saturation, @lightness, @alpha);',
			'description': 'creates a color'
		},
		{
			'name': 'hsv',
			'example': 'hsv(@hue, @saturation, @value);',
			'description': 'creates a color'
		},
		{
			'name': 'hsva',
			'example': 'hsva(@hue, @saturation, @value, @alpha);',
			'description': 'creates a color'
		},
		{
			'name': 'hue',
			'example': 'hue(@color);',
			'description': 'returns the `hue` channel of `@color` in the HSL space'
		},
		{
			'name': 'saturation',
			'example': 'saturation(@color);',
			'description': 'returns the `saturation` channel of `@color` in the HSL space'
		},
		{
			'name': 'lightness',
			'example': 'lightness(@color);',
			'description': 'returns the `lightness` channel of `@color` in the HSL space'
		},
		{
			'name': 'hsvhue',
			'example': 'hsvhue(@color);',
			'description': 'returns the `hue` channel of `@color` in the HSV space'
		},
		{
			'name': 'hsvsaturation',
			'example': 'hsvsaturation(@color);',
			'description': 'returns the `saturation` channel of `@color` in the HSV space'
		},
		{
			'name': 'hsvvalue',
			'example': 'hsvvalue(@color);',
			'description': 'returns the `value` channel of `@color` in the HSV space'
		},
		{
			'name': 'red',
			'example': 'red(@color);',
			'description': 'returns the `red` channel of `@color`'
		},
		{
			'name': 'green',
			'example': 'green(@color);',
			'description': 'returns the `green` channel of `@color`'
		},
		{
			'name': 'blue',
			'example': 'blue(@color);',
			'description': 'returns the `blue` channel of `@color`'
		},
		{
			'name': 'alpha',
			'example': 'alpha(@color);',
			'description': 'returns the `alpha` channel of `@color`'
		},
		{
			'name': 'luma',
			'example': 'luma(@color);',
			'description': 'returns the `luma` value (perceptual brightness) of `@color`'
		},
		{
			'name': 'saturate',
			'example': 'saturate(@color, 10%);',
			'description': 'return `@color` 10% points more saturated'
		},
		{
			'name': 'desaturate',
			'example': 'desaturate(@color, 10%);',
			'description': 'return `@color` 10% points less saturated'
		},
		{
			'name': 'lighten',
			'example': 'lighten(@color, 10%);',
			'description': 'return `@color` 10% points lighter'
		},
		{
			'name': 'darken',
			'example': 'darken(@color, 10%);',
			'description': 'return `@color` 10% points darker'
		},
		{
			'name': 'fadein',
			'example': 'fadein(@color, 10%);',
			'description': 'return `@color` 10% points less transparent'
		},
		{
			'name': 'fadeout',
			'example': 'fadeout(@color, 10%);',
			'description': 'return `@color` 10% points more transparent'
		},
		{
			'name': 'fade',
			'example': 'fade(@color, 50%);',
			'description': 'return `@color` with 50% transparency'
		},
		{
			'name': 'spin',
			'example': 'spin(@color, 10);',
			'description': 'return `@color` with a 10 degree larger in hue'
		},
		{
			'name': 'mix',
			'example': 'mix(@color1, @color2, [@weight: 50%]);',
			'description': 'return a mix of `@color1` and `@color2`'
		},
		{
			'name': 'greyscale',
			'example': 'greyscale(@color);',
			'description': 'returns a grey, 100% desaturated color',
		},
		{
			'name': 'contrast',
			'example': 'contrast(@color1, [@darkcolor: black], [@lightcolor: white], [@threshold: 43%]);',
			'description': 'return `@darkcolor` if `@color1 is> 43% luma` otherwise return `@lightcolor`, see notes'
		},
		{
			'name': 'multiply',
			'example': 'multiply(@color1, @color2);'
		},
		{
			'name': 'screen',
			'example': 'screen(@color1, @color2);'
		},
		{
			'name': 'overlay',
			'example': 'overlay(@color1, @color2);'
		},
		{
			'name': 'softlight',
			'example': 'softlight(@color1, @color2);'
		},
		{
			'name': 'hardlight',
			'example': 'hardlight(@color1, @color2);'
		},
		{
			'name': 'difference',
			'example': 'difference(@color1, @color2);'
		},
		{
			'name': 'exclusion',
			'example': 'exclusion(@color1, @color2);'
		},
		{
			'name': 'average',
			'example': 'average(@color1, @color2);'
		},
		{
			'name': 'negation',
			'example': 'negation(@color1, @color2);'
		}
	];


	constructor() {
		super('@');
	}

	private createFunctionProposals(proposals: IFunctionInfo[], existingNode: nodes.Node, sortToEnd: boolean, result: CompletionList): CompletionList {
		for (let p of proposals) {
			let item: CompletionItem = {
				label: p.name,
				detail: p.example,
				documentation: p.description,
				textEdit: TextEdit.replace(this.getCompletionRange(existingNode), p.name + '($0)'),
				insertTextFormat: InsertTextFormat.Snippet,
				kind: CompletionItemKind.Function
			};
			if (sortToEnd) {
				item.sortText = 'z';
			}
			result.items.push(item);
		}
		return result;
	}


	public getTermProposals(entry: languageFacts.IEntry, existingNode: nodes.Node, result: CompletionList): CompletionList {
		let functions = LESSCompletion.builtInProposals;
		if (entry) {
			functions = functions.filter(f => !f.type || entry.restrictions.indexOf(f.type) !== -1);
		}
		this.createFunctionProposals(functions, existingNode, true, result);
		return super.getTermProposals(entry, existingNode, result);
	}

	protected getColorProposals(entry: languageFacts.IEntry, existingNode: nodes.Node, result: CompletionList): CompletionList {
		this.createFunctionProposals(LESSCompletion.colorProposals, existingNode, false, result);
		return super.getColorProposals(entry, existingNode, result);
	}
	
	public getCompletionsForDeclarationProperty(declaration: nodes.Declaration, result: CompletionList): CompletionList {
		this.getCompletionsForSelector(null, true, result);
		return super.getCompletionsForDeclarationProperty(declaration, result);
	}	

}

